/*
	Project: Socket_lib
	File: app_client_main.cpp
	Purpose: To send and recieve text files from the FTP server
	Authors: Brooke Thrower and Jeramie Hallyburton
	Date: April 6, 2011
*/

#define _SCL_SECURE_NO_WARNINGS //added to use std::copy() without warnings
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
using namespace std;

#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;

#include <sys/stat.h>
#include "../include/socket.hpp"

#include "../include/helper.hpp"

bool SendFile( fs::path sourcePath, Socket &socket )
{	
	//send message to server requesting to send file
	char sendBuf[32] = "Sending";
	char recvBuf[32];
	socket.Send( sendBuf );

	socket.Receive( recvBuf, 32 );

	if( strcmp( recvBuf, "OK" ) != 0 )
	{
		return false;
	}

	/* open file for reading in binary mode */
	ifstream infile;
	infile.open( sourcePath.string().c_str(), std::ios::in | std::ios::binary );

	if (infile.fail() == 1)
	{
		infile.close();
		return false;
	}	

	//get filesize as string
	__int64 size = fs::file_size(sourcePath);
	string filesize;
	stringstream out;
	out << size;
	filesize = out.str();

	//send size of file to server and the filename
	string send = sourcePath.filename() + " " + filesize;
	char sendFileInfo[1024];
	int bytesRead = send.size();
		if( bytesRead < 1024 )
			sendFileInfo[bytesRead] = '\0';
	
	send.copy( sendFileInfo, send.length(), 0 );
	socket.Send( sendFileInfo );
	socket.Receive( recvBuf, 32 ); //check for ok message

	//start sending file in chunks to server
	/* set cursor to the beginning of the file */
    infile.seekg(0, std::ios::beg);

	//file will be broken into chunks of 1024 bytes
	while (!infile.eof())
    {
		char   buf[1024];
		infile.read( buf, sizeof(buf) - 1 );
		int bytesRead = (int)infile.gcount();
		buf[bytesRead] = '\0';
		socket.Send( buf );

		//recieve ack from server
		socket.Receive( recvBuf, 32 );

		if( strcmp( recvBuf, "OK" ) != 0 )
		{
			return false;
		}
    }

	//send eof message
	socket.Send( "End" );
	infile.close();
	socket.Receive( recvBuf, 32 );
	if(	 strcmp( recvBuf, "Done") == 0 )
	{
		return true;
	}
	else
	{
		return false;
	}
}

bool RecvFile( string filename, string saveLoc , Socket &socket )
{
	//send message to server requesting to send file
	char sendBuf[32] = "Receiving";
	char recvBuf[32];
	socket.Send( sendBuf );

	socket.Receive( recvBuf, 32 );

	if( strcmp( recvBuf, "OK" ) != 0 )
	{
		return false;
	}

	//send the name of file to pull from server
	char sendFilename[1024];

	int bytesRead = filename.size();
	sendFilename[bytesRead] = '\0';
	
	filename.copy( sendFilename, filename.length(), 0 ); //needed to convert to char[]

	socket.Send( sendFilename );

	//wait for reply ( reply contains filesize for testing later)
	unsigned long filesize;
	char recvSizeBuf[1024];
	socket.Receive( recvSizeBuf, 1024 );
	if( strcmp( recvSizeBuf, "Error" ) == 0)
		return false;
	istringstream iss(recvSizeBuf);
	iss >> filesize;

	char sendBeginBuf[32] = "BeginSend";
	socket.Send( sendBeginBuf );

	ofstream outfile;
	string file = saveLoc + filename;
	outfile.open( file, std::ios::out | std::ios::binary );

	while ( true )
	{
		char recvBuf[1024];
		socket.Receive( recvBuf, 1024 );
		if( strcmp( recvBuf, "End" ) == 0 )
		{
			outfile.close();
			break;
		}
		stringstream ss(recvBuf);
		outfile.write( ss.str().c_str(), ss.str().length() );
		//send message back to client
		char sendbuf[32] = "OK";
		socket.Send( sendbuf );
	}

	//check if files match
	struct stat FileInfo;
	if ( stat( file.c_str(), &FileInfo ) == 0 )
	{
			file = (char)FileInfo.st_size;
	}

	if( filesize == FileInfo.st_size )
	{
		return true;
	}
	else
	{
		return false;
	}
}

//test that the socket library is working
string testTransfer() 
{
	//create test file
	ofstream outFile( "transfer-large.txt" );
	for( unsigned i = 0; i < 10000000; ++i ) 
	{
		outFile << i;
	}
	outFile.close();

	return "transfer-large.txt";
}


int main() 
{

	cout << "Client app running..." << endl;
	Socket newSock( AF_INET, IPPROTO_TCP );
	string line;
	string server;

	cout << "Enter the IP of Server: ";
	while( getline(cin, line) )
	{
		istringstream iss(line);
		iss >> server;

		//create new connection to server
		if(newSock.Connect( server, 8478 )) {
			char recvBuf[32];
			newSock.Receive( recvBuf, 32 );
			cout << recvBuf << endl;
			break;
		}

		cerr << "Could not connect to the server provided." << endl;
		cout << "Enter the IP of Server: ";
	}

	cin.clear();

	string cmd;
	string param, param2;
	cout << "Enter a command (quit, test, send [path+filename]\n or receive [filename] [saveLocation]):"<< endl;
	//determine action based on parameters
	while( getline(cin, line) )
	{
		ParseCommand( line, cmd, param, param2 );
		//test
		if( cmd == "TEST" ) {
			fs::path sourcePath( testTransfer() );

			if( SendFile( sourcePath , newSock ))
				cout << "Sent Successful!"<<endl;
			else
				cerr << "Sending Failed." <<endl;
		}
		//send file
		else if(  cmd == "SEND" )
		{
			if( param != "" ) {
				fs::path sourcePath( param );

				if( SendFile( sourcePath , newSock ))
					cout << "Sent Successful!"<<endl;
				else
					cerr << "Sending Failed." <<endl;
			} else {
				cerr << "Missing parameter for send." << endl;
			}
		}
		//get file
		else if ( cmd == "RECEIVE" )
		{
			string file = param;
			
			//test if location exists to save to
			if( fs::exists( fs::path(param2)  ) )
			{
				if( RecvFile( param, param2, newSock ) )
				{
					cout << "File Successfully Recieved!"<<endl;
				}
				else
				{
					cerr << "File Retreival Failed." <<endl;
				}
			}
			else
			{
				cerr << "Invalid save location"<<endl;
			}
		}
		//end program
		else if( cmd == "QUIT" ) {
			char sendBuf[32] = "Quit";
			newSock.Send( sendBuf );
			break;
		}
		else
		{
			cerr <<"Invalid command." << endl;
		}

		cout << "Enter a command (quit, test, send [path+filename]\n or receive [filename] [saveLocation]):"<< endl;
	}
}